/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/

/**
 * @brief Implement type inference for AMC.
 *
 * @author  WXQ#XDU
 * @date    2024.12
 */

#include "../include/typesys.h"


t_type_ptr  type_infer ( EnumTokenKind op ,
                         t_type_ptr type1 ,
                         t_type_ptr type2 )
{
    switch (op) {
        case TK_INT_LITERAL:
            return type_new_baseType(TK_TYPE_INT);
        case TK_REAL_LITERAL:
            return type_new_baseType(TK_TYPE_DOUBLE);

        case TK_ID:   // name of a scalar, array, function
        case TK_OP_ASSIGN:      // ID  = ...
        case TK_OP_PLUSASSIGN:  // ID += ...
        case TK_OP_MINUSASSIGN: // ID -= ...
        case TK_OP_MULASSIGN:   // ID *= ...
        case TK_OP_DIVASSIGN:   // ID /= ...
        case TK_OP_MODASSIGN:   // ID %= ...
        case TK_LPAREN:         // for "(e)", return the type of e
            return type1;

        case TK_OP_AND:     // &&
        case TK_OP_OR:      // ||
        case TK_OP_EQ:      // ==
        case TK_OP_NE:      // !=
        case TK_OP_GE:      // >=
        case TK_OP_GT:      // >
        case TK_OP_LE:      // <=
        case TK_OP_LT:      // <
            if (NULL == type2)
                return type1;
            else
                return type_new_baseType(TK_TYPE_INT);

        case TK_OP_NOT:     // !
            return type_new_baseType(TK_TYPE_INT);

        case TK_OP_PLUS:
        case TK_OP_MINUS:
            if (NULL == type2)  // +x, -x
                return type1;
            // the control flow continues down for binary operators

        case TK_OP_MUL:
        case TK_OP_DIV:
        case TK_OP_MOD:
        {
            EnumTokenKind t1 = type_var_basekind(type1);
            EnumTokenKind t2 = type_var_basekind(type2);
            if ( TK_TYPE_DOUBLE == t1 || TK_TYPE_DOUBLE == t2 )
                return type_new_baseType(TK_TYPE_DOUBLE);
            else
                return type_new_baseType(TK_TYPE_INT);
        }

        default:
            break;
    }

    return type1;
}

EnumTokenKind  type_infer_enum ( EnumTokenKind op    ,
                                 EnumTokenKind type1 ,
                                 EnumTokenKind type2 )
{
    t_type_ptr type1Ptr = NULL;
    t_type_ptr type2Ptr = NULL;
    if (TK_TYPE_MIN__ < type1 && type1 < TK_TYPE_MAX__)
        type1Ptr = type_new_baseType(type1);
    else
        return type1;

    if (TK_TYPE_MIN__ < type2 && type2 < TK_TYPE_MAX__)
        type2Ptr = type_new_baseType(type2);
    else
        return type1;

    t_type_ptr result = type_infer(op, type1Ptr, type2Ptr);
    return type_var_basekind(result);
}
